import 'package:flutter/cupertino.dart';
import 'package:heytea_kit/logger.dart';
import 'package:pull_to_refresh/pull_to_refresh.dart';

typedef Future<LoadingResult> OnLoad(int page);

enum LoadingResult {
  hasMore,
  hasDataButNoMore,
  noData,
}

class Refresher extends StatefulWidget {
  static const firstPage = 1;

  final RefreshController controller;
  final Widget child;
  final Widget header;
  final Widget footer;
  final bool enablePullDown;
  final bool enablePullUp;
  final bool primary;
  final bool reverse;
  final Axis scrollDirection;
  final ScrollController scrollController;

  final OnLoad onLoad;
  final bool loadMoreWhenNotFull;

  Refresher({
    Key key,
    @required this.controller,
    @required this.child,
    this.header = const ClassicHeader(
      refreshStyle: RefreshStyle.Behind,
      releaseText: '',
      refreshingText: '',
      canTwoLevelText: '',
      completeText: '',
      failedText: '',
      idleText: '',
      refreshingIcon: CupertinoActivityIndicator(),
    ),
    this.footer = const ClassicFooter(
      loadStyle: LoadStyle.ShowWhenLoading,
      loadingText: '',
      noDataText: '',
      idleText: '',
      failedText: '',
      canLoadingText: '',
      loadingIcon: CupertinoActivityIndicator(),
    ),
    this.enablePullDown = true,
    this.enablePullUp = true,
    this.primary,
    this.reverse = false,
    this.scrollDirection,
    this.scrollController,
    @required this.onLoad,
    this.loadMoreWhenNotFull = true,
  }) : super(key: key);

  @override
  State<StatefulWidget> createState() {
    return _RefresherState();
  }
}

class _RefresherState extends State<Refresher> {
  LoadingResult _loadingResult = LoadingResult.hasMore;
  int _page = 0;

  @override
  Widget build(BuildContext context) {
    return SmartRefresher(
      controller: widget.controller,
      child: widget.child,
      header: widget.header,
      footer: widget.footer,
      enablePullDown: widget.enablePullDown,
      enablePullUp: widget.enablePullUp && _loadingResult == LoadingResult.hasMore,
      onRefresh: _onRefresh,
      onLoading: _onLoading,
      primary: widget.primary,
      reverse: widget.reverse,
      scrollDirection: widget.scrollDirection,
      scrollController: widget.scrollController,
    );
  }

  bool isNotFull() {
    final scrollController = widget.controller.scrollController;

    if (scrollController.hasClients == false) return false;

    final position = scrollController.position;
    return position.minScrollExtent == 0 && position.maxScrollExtent == 0;
  }

  void _onRefresh() async {
    try {
      final result = await _loadPage(Refresher.firstPage);

      if (mounted == false) return;

      setState(() {
        _loadingResult = result;
      });
      widget.controller.refreshCompleted(
        resetFooterState: _loadingResult == LoadingResult.hasMore,
      );
    } catch (error) {
      HeyTeaLogger().e('刷新时出错', error);

      if (mounted == false) return;

      widget.controller.refreshFailed();
    }
  }

  void _onLoading() async {
    try {
      final result = await _loadPage(_page + 1);

      if (mounted == false) return;

      setState(() {
        _loadingResult = result;
      });
      switch (result) {
        case LoadingResult.hasMore:
          widget.controller.loadComplete();
          break;
        case LoadingResult.hasDataButNoMore:
        case LoadingResult.noData:
          widget.controller.loadNoData();
          break;
      }
    } catch (error) {
      HeyTeaLogger().e('加载时出错', error);

      if (mounted == false) return;

      widget.controller.loadFailed();
    }
  }

  Future<LoadingResult> _loadPage(int nextPage) async {
    bool shouldContinue = true;

    LoadingResult result;
    while (shouldContinue) {
      result = await widget.onLoad(nextPage);
      if (result != LoadingResult.noData) _page = nextPage;

      shouldContinue = widget.loadMoreWhenNotFull &&
          result == LoadingResult.hasMore &&
          await Future.delayed(const Duration(milliseconds: 100), isNotFull) &&
          mounted;

      if (shouldContinue) nextPage += 1;
    }

    return result;
  }
}
